/*
Buffer Depth Calculation:
format of in2.txt
<dma_channels> <no of perips> <max_burst> <round_trip_latency_cycles> <clk_rate> <min_depth>
gcc buffer.c -o buffer -lm -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition
*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#define USE_CASE_COUNT 32

struct channel
{
  char   name[64];
  double buffer_depth;
  double worst_case_depth;
  double txn_rate;
  double ratio;
  double worst_ratio;
  char preload;
  char fail;
  int worst_case;
  int worst_latency;
  char direction;
  char buffer_width;
  char use_case[USE_CASE_COUNT];
};


unsigned int Power_Two(unsigned int x);
unsigned int find_fastest(struct channel peripherals[], int no_of_channels, FILE *file);
double biggest(double a, double b);
void print_logo(FILE *file);

int main(int argc, char *argv[])
{
  //OPEN FILES
  FILE *file_in;
  if (argc>1)
    {
      file_in=fopen(argv[1],"r");
    }
  else
    {
      printf(" No Filename Provided \n");
      exit(1);
    }
  FILE *file_out;
  file_out = fopen(argv[2],"w");
  if(file_in==NULL|| file_out==NULL)
    {
      // printf(" Can't Open Input File\n");
      fprintf(stderr,"Can't Open Input File \r\n");
      exit(1);
    }
  print_logo(file_out);
  //INSTANTIATE VARIABLES
  double clock_rate=0;
  int round_trip_rd=0;
  int round_trip_wr=0;
  double min_depth=0;
  int lines=0;
  int dma_channels=0;
  int max_burst_file=0;
  int max_system_bursts=8;
  int max_burst_real=0;
  int fastest_perip=0;
  int i=0;
  int j=0;
  int use_case_channels[USE_CASE_COUNT];
  double effective_dma_rate=0;
  double effective_perip_rate=0;
  double max_perip_rate=0;
  int round_trip_total=0;
  double ratio=0;
  double burst=0;
  double depth=0;
  double freq_w=0;
  int round_trip_depth=0;
  double depth_temp=0;
  char temp_use_cases[5];
  //PARSE IN FILE AND ASSIGN VARIABLES

  int counter=0;
  int char_counter=0;
  char *result;
  char temp;
  char temp_line[200];
  char **end;
  char delim[]=",";
  int largest_result=0;
  int index=0;
  int c;
  int toggle=0;
  
  while (fgets(temp_line,sizeof temp_line,file_in))
    {
      if (*temp_line=='#'|| *temp_line=='\n') continue;
      else lines ++;
    }
  
  lines=lines-1;
  struct channel perips[lines];
  rewind(file_in);

  while (fgets(temp_line,sizeof temp_line,file_in))
    {
      if (*temp_line=='#'|| *temp_line=='\n') continue;
      else if (toggle==0)
	{
	  sscanf(temp_line,"%i %i %i %i %lf"
		 ,&dma_channels,&max_burst_file,&round_trip_rd,&round_trip_wr,&clock_rate);
	  printf("\n ----------------------------------\n");
	  fprintf(file_out,"\r\n ----------------------------------\r\n");
	  printf(" %i Peripherals in File \n ----------------------------------\n",lines);
	  fprintf(file_out," %i Peripherals in File \r\n ----------------------------------\r\n",lines);
	  toggle=1;
	}
      else if(sscanf(temp_line,"%s %lf %c %c %c %s",&perips[counter].name,&perips[counter].txn_rate,&perips[counter].direction,
		     &perips[counter].preload,&perips[counter].buffer_width, &temp_use_cases)==6)
	{
	  result = (char *)malloc(sizeof(char));
	  result=strtok(temp_use_cases,delim);
	  if (result[1]=='\000')
	    index=*result-'0';
	  else index = (result[0]-'0')*10 + result[1]-'0';
	  perips[counter].use_case[index-1]='Y';
	  while(result!=NULL)   
	    {
	      if (largest_result<index)largest_result=index;
	      result=strtok(NULL,delim);
	      if (result!=NULL)
		{
		  //ndex=*result - '0';
		  if (result[1]=='\000')
		    index=*result-'0';
		  else index = (result[0]-'0')*10 + result[1]-'0';
		  perips[counter].use_case[index-1]='Y';
		}
	    }
	  
	  for (char_counter=0;char_counter<USE_CASE_COUNT;char_counter++)
	    {
	      if(perips[counter].use_case[char_counter]!='Y')perips[counter].use_case[char_counter]='N';
	    }
	  printf(" [%i] %s running at %.2f MBps\n",counter+1,perips[counter].name,perips[counter].txn_rate);
	  fprintf(file_out," [%i] %s running at %.2f MBps\r\n",counter+1,perips[counter].name,perips[counter].txn_rate);
	  counter++;
	}

     
    }

free (result);
printf(" ----------------------------------\n");
fprintf(file_out," ----------------------------------\r\n");
//sleep(1);
fclose(file_in);
fastest_perip=0;
fastest_perip=find_fastest(perips,lines,file_out);
printf(" ----------------------------------\n");
fprintf(file_out," ----------------------------------\r\n");
round_trip_total=round_trip_rd*dma_channels;


printf(" System Clock Rate : %.2fMHz \n",clock_rate);
fprintf(file_out," System Clock Rate : %.2fMHz\r\n",clock_rate);
printf(" Worst Case Round Trip Read Latency : %i Cycles \n",round_trip_rd);
fprintf(file_out," Worst Case Round Trip Read Latency : %i Cycles \r\n",round_trip_rd);
printf(" Worst Case Round Trip Write Latency : %i Cycles \n",round_trip_wr);
fprintf(file_out," Worst Case Round Trip Write Latency : %i Cycles \r\n",round_trip_wr);
printf(" Active DMA Channels : %i \n",dma_channels);
  fprintf(file_out," Active DMA Channels : %i\r\n",dma_channels);
  printf(" ----------------------------------\n\n");
  fprintf(file_out," ----------------------------------\r\n\r\n");

  

  /////CHECK THROUGH EACH USE CASE AND PERFORM ANALYSIS

  for (i=0;i<USE_CASE_COUNT;i++)
    {
      use_case_channels[i]=0;
    }

  for (i=0;i<lines;i++)
    {
      perips[i].worst_case_depth=0;
      for (j=0;j<largest_result;j++)
	{
	  if(perips[i].use_case[j]=='Y')use_case_channels[j]++;
	}
    }

  for (i=0;i<largest_result;i++)
    {
      round_trip_total=0;
      printf("\n == Use Case %i ================================= \n",i+1);
      fprintf(file_out,"\n == Use Case %i ================================ \r\n",i+1);
      printf(" Results for the Specified Max. Burst - %i \n",max_burst_file);
      fprintf(file_out," Results for the Specified Max. Burst - %i \r\n",max_burst_file);
      for (j=0;j<lines;j++)
	{
	  if(perips[j].use_case[i]=='Y')
	    {
	      if(perips[j].direction=='R')
		round_trip_total+=round_trip_rd;
	      else
		round_trip_total+=round_trip_wr;
	    }
	}
      effective_dma_rate = clock_rate*max_burst_file/round_trip_total;
      printf(" With Total Round Trip Latency - %i \n",round_trip_total);
      fprintf(file_out," With Total Round Trip Latency - %i \n",round_trip_total);
      printf(" Effective DMA Service Rate : %.2f MWps\n",effective_dma_rate);
      fprintf(file_out," Effective DMA Service Rate : %.2f MWps\r\n",effective_dma_rate);
      printf("  _______________________________________________________________________________________________________\n");
      printf(" |         Peripheral             | Direction |  Txn. Rate  |   Ratio        |  Preload  | Buffer Depth  |\n");
      printf(" | _______________________________|___________|____(MBps)___|________________|___________|______________ |\n");
      fprintf(file_out,"  _______________________________________________________________________________________________________\r\n");
      fprintf(file_out," |         Peripheral             | Direction |  Txn. Rate  |   Ratio        |  Preload  | Buffer Depth  |\r\n");
      fprintf(file_out," | _______________________________|___________|____(MBps)___|________________|___________|______________ |\r\n");
      for (j=0;j<lines;j++)
	{
	  if(perips[j].use_case[i]=='Y')
	    {
	       effective_dma_rate = clock_rate*max_burst_file/round_trip_total;
	       if (perips[j].buffer_width=='W') 
		 perips[j].ratio=(perips[j].txn_rate/4)/effective_dma_rate;
	       else
		  perips[j].ratio=(perips[j].txn_rate)/effective_dma_rate;
	       perips[j].fail='N';
	       perips[j].buffer_depth = (ceil(perips[j].ratio)+1)*max_burst_file;
	       
	       if (perips[j].buffer_depth>=perips[j].worst_case_depth)
		 {
		   perips[j].worst_case_depth=perips[j].buffer_depth;
		   perips[j].worst_case=i;
		   perips[j].worst_ratio= perips[j].ratio;
		   perips[j].worst_latency=round_trip_total;
		 }
	       
	       if (perips[j].preload=='Y')
		 {
		   if(perips[j].buffer_width=='W')
		     {
		       printf(" \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Words\n",
			      perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"Yes",perips[j].buffer_depth);
		       fprintf(file_out," \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Words\r\n",
			       perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"Yes",perips[j].buffer_depth);
		     }
		   else
		     {
		       printf(" \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Bytes\n",
			      perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"Yes",perips[j].buffer_depth);
		       fprintf(file_out," \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Bytes\r\n",
			       perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"Yes",perips[j].buffer_depth);
		     }
		 }
	       else if(perips[j].preload=='N')
		 {
		   if(perips[j].buffer_width=='W')
		     {
		       printf(" \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Words",
			      perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"No",perips[j].buffer_depth);
		       fprintf(file_out," \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Words",
			       perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"No",perips[j].buffer_depth);
		     }
		   else
		     {
		       printf(" \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Bytes",
			      perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"No",perips[j].buffer_depth);
		       fprintf(file_out," \t%-16s \t\t%c \t%.2f \t\t%.3f     \t%-3s \t    %.0f Bytes",
			       perips[j].name,perips[j].direction,perips[j].txn_rate,perips[j].ratio,"No",perips[j].buffer_depth);
		     }
		   if(perips[j].ratio>1)
		     {
		       printf(" **FAILURE**");
		       perips[j].fail='Y';
		       fprintf(file_out," **FAILURE**");
		     }
		   printf(" \n");
		   fprintf(file_out," \r\n");
		 }
	    }
	}
      printf(" _________________________________________________________________________________________________________\n");
      fprintf(file_out," _________________________________________________________________________________________________________\r\n");
      
    }

//CHECK DATA RATES USING FILE PARAMS
  printf("\n Worst Case Depths for the Specified Max. Burst - %i \n",max_burst_file);
  fprintf(file_out,"\r\n Worst Case Depths for the Specified Max. Burst - %i \r\n",max_burst_file);
  printf("  ___________________________________________________________________________________________________________________\n");
  printf(" |         Peripheral             |   Worst   |  Txn. Rate  |   Ratio        |  Preload  | Buffer Depth  | Use Case  |\n");
  printf(" | _______________________________|__Latency__|____(MBps)___|________________|___________|______________ |___________|\n");
  fprintf(file_out,"  ___________________________________________________________________________________________________________________\r\n");
  fprintf(file_out," |         Peripheral             |   Worst   |  Txn. Rate  |   Ratio        |  Preload  | Buffer Depth  | Use Case  |\r\n");
  fprintf(file_out," | _______________________________|__Latency__|____(MBps)___|________________|___________|______________ |___________|\r\n");
  
   for (i=0;i<lines;i++)
     {
       perips[i].fail='N';
       if (perips[i].preload=='Y')
 	{
 	  printf(" \t%-16s \t\t%i \t%.2f \t\t%.3f     \t%-3s \t\t  %.0f\t\t%i \n",
		 perips[i].name,perips[i].worst_latency,perips[i].txn_rate,perips[i].worst_ratio,"Yes",perips[i].worst_case_depth,perips[i].worst_case+1);
	  fprintf(file_out," \t%-16s \t\t%i \t%.2f \t\t%.3f    \t%-3s \t\t  %.0f\t\t%i \r\n",
		  perips[i].name,perips[i].worst_latency,perips[i].txn_rate,perips[i].worst_ratio,"Yes",perips[i].worst_case_depth,perips[i].worst_case+1);
	}
       else if(perips[i].preload=='N')
	{
	  printf(" \t%-16s \t\t%i \t%.2f \t\t%.3f     \t%-3s \t\t  %.0f\t\t%i ",
		 perips[i].name,perips[i].worst_latency,perips[i].txn_rate,perips[i].worst_ratio,"No",perips[i].worst_case_depth,perips[i].worst_case+1);
	  fprintf(file_out," \t%-16s \t\t%i \t%.2f \t\t%.3f     \t%-3s \t\t  %.0f\t\t%i ",
		  perips[i].name,perips[i].worst_latency,perips[i].txn_rate,perips[i].ratio,"No",perips[i].worst_case_depth,perips[i].worst_case+1);
	  
	  if(perips[i].worst_ratio>1)
	    {
	      printf(" **FAILURE**");
	      perips[i].fail='Y';
	      fprintf(file_out," **FAILURE**");
	    }
	  printf(" \n");
	  fprintf(file_out," \r\n");
	}
    }
  printf("\n");
  fprintf(file_out,"\r\n");
  
  fclose(file_out);
  
  
}


unsigned int find_fastest(struct channel peripherals[], int no_of_channels, FILE *file)
{
  int index=0;
  int i;
  int temp=0;
  for (i=0;i<no_of_channels;i++)
    {
      if (peripherals[i].txn_rate>temp)
	{
	  index=i;
	  temp=peripherals[i].txn_rate;
	}
    }
  printf(" Fastest Perip. is %s on channel %i, with Txn. Rate %.2f\n",peripherals[index].name,index+1,peripherals[index].txn_rate);
  fprintf(file," Fastest Perip. is %s on channel %i, with Txn. Rate %.2f\r\n",peripherals[index].name,index+1,peripherals[index].txn_rate);
  //sleep(2);
  return index;
}
unsigned int Power_Two(unsigned int x)
{
  unsigned int i=0;
  unsigned int result=0;
  while (result<x)
    {
      i++;
      result=pow(2,i);
    }
  return result;
}

double biggest(double a, double b)
{
  if (a>=b)
    return a;
  else return b;
}


void print_logo (FILE *file)
{
  fprintf(file,"\r\n");
  fprintf(file,"                             t@@@@@@f                                                       \r\n");
  fprintf(file,"                          f@@@@@@@@@@@@@@C                                                  \r\n");
  fprintf(file,"                        8@@@@:        .@@@@@                                                \r\n");
  fprintf(file,"                       @@@@              8@@@                                               \r\n");
  fprintf(file,"                      @@@i                .@@@  @@@@@@@@@@@@@@@@@@@@                        \r\n");
  fprintf(file,"                     G@@8                  C@@@                  @@@                        \r\n");
  fprintf(file,"                     @@@,                   @@@                  @@@                        \r\n");
  fprintf(file,"                     @@@:                   @@@                  @@@                        \r\n");
  fprintf(file,"                     i@@@                   @@@                  @@@                        \r\n");
  fprintf(file,"                      0@@@                  @@@                  @@@                        \r\n");
  fprintf(file,"                       C@@@0            C@  @@@                  @@@                        \r\n");
  fprintf(file,"                         @@@@@8i    ;8@@@@  @@@                  @@@                        \r\n");
  fprintf(file,"                           :@@@@@@@@@@@@i   @@@                  @@@                        \r\n");
  fprintf(file,"                                            @@@                  @@@                        \r\n");
  fprintf(file,"                                            @@@@@@@@@@@@@@@@@@@@@@@@                        \r\n");
  fprintf(file,"                                            @@@@@@@@@@@@@@@@@@@@@@@@                        \r\n");
  fprintf(file,"                                                                                            \r\n");
  fprintf(file,"                                                                                            \r\n");
  fprintf(file," .@t                                                              @@                        \r\n");
  fprintf(file," @@t @@@@@@t0@@@@i   C@@@@C@@   8@@@@,@@ @@t @@@@@@@    @@@@@ @8 @@@@ @@G   @@@@@.  @@0@@@@ \r\n");
  fprintf(file," @@t @@;  @@@   @@1 @@L   0@@ 1@@.   @@@ @@t @@    C@ @@@   .@@8  @@  @@G @@L   @@@ @@8   @@\r\n");
  fprintf(file," @@t @@   1@@   G@L @@     @@ @@:     @@ @@t @@    @@ @@     i@8  @@  @@G @0     @@ @@,   @@\r\n");
  fprintf(file," @@t @@   1@@   G@L @@i   L@@ t@@    @@@ @@t @@    @@ @@@    @@8  @@  @@G @@;   0@@ @@,   @@\r\n");
  fprintf(file," @@t @@   1@@   G@L  @@@@@@@@    @@@@L@@ @@t @@    @@  i@@@@@i@8  @@  @@G  G@@@@@   @@,   @@\r\n");
  fprintf(file,"                              t@    i@@                                                    \r\n");
  fprintf(file,"                                1@@@@                                                      \r\n");
  
  fprintf(file,"\r\n");
  fprintf(file,"\t\t\t####################################################\r\n");
  fprintf(file,"\t\t\t#              Buffer Depth Calculator             #\r\n");
  fprintf(file,"\t\t\t#         Imagination Technologies Group PLC       #\r\n");
  fprintf(file,"\t\t\t#                   IMGWorks 2013                  #\r\n");
  fprintf(file,"\t\t\t####################################################\r\n");
  
  
  printf("\n");
  printf("                             t@@@@@@f                                                       \n");
  printf("                          f@@@@@@@@@@@@@@C                                                  \n");
  printf("                        8@@@@:        .@@@@@                                                \n");
  printf("                       @@@@              8@@@                                               \n");
  printf("                      @@@i                .@@@  @@@@@@@@@@@@@@@@@@@@                        \n");
  printf("                     G@@8                  C@@@                  @@@                        \n");
  printf("                     @@@,                   @@@                  @@@                        \n");
  printf("                     @@@:                   @@@                  @@@                        \n");
  printf("                     i@@@                   @@@                  @@@                        \n");
  printf("                      0@@@                  @@@                  @@@                        \n");
  printf("                       C@@@0            C@  @@@                  @@@                        \n");
  printf("                         @@@@@8i    ;8@@@@  @@@                  @@@                        \n");
  printf("                           :@@@@@@@@@@@@i   @@@                  @@@                        \n");
  printf("                                            @@@                  @@@                        \n");
  printf("                                            @@@@@@@@@@@@@@@@@@@@@@@@                        \n");
  printf("                                            @@@@@@@@@@@@@@@@@@@@@@@@                        \n");
  printf("                                                                                            \n");
  printf("                                                                                            \n");
  printf(" .@t                                                              @@                        \n");
  printf(" @@t @@@@@@t0@@@@i   C@@@@C@@   8@@@@,@@ @@t @@@@@@@    @@@@@ @8 @@@@ @@G   @@@@@.  @@0@@@@ \n");
  printf(" @@t @@;  @@@   @@1 @@L   0@@ 1@@.   @@@ @@t @@    C@ @@@   .@@8  @@  @@G @@L   @@@ @@8   @@\n");
  printf(" @@t @@   1@@   G@L @@     @@ @@:     @@ @@t @@    @@ @@     i@8  @@  @@G @0     @@ @@,   @@\n");
  printf(" @@t @@   1@@   G@L @@i   L@@ t@@    @@@ @@t @@    @@ @@@    @@8  @@  @@G @@;   0@@ @@,   @@\n");
  printf(" @@t @@   1@@   G@L  @@@@@@@@    @@@@L@@ @@t @@    @@  i@@@@@i@8  @@  @@G  G@@@@@   @@,   @@\n");
  printf("                              t@    i@@                                                    \n");
  printf("                                1@@@@                                                      \n");
  
  printf("\n");
  printf("\t\t\t####################################################\n");
  printf("\t\t\t#              Buffer Depth Calculator             #\n");
  printf("\t\t\t#         Imagination Technologies Group PLC       #\n");
  printf("\t\t\t#                   IMGWorks 2013                  #\n");
  printf("\t\t\t####################################################\n");
  
  
  sleep(1);
}
